import turtle as t
import math

class Pen:
    def __init__(
        self,
        color:str='black',
        size:int=1,
    ) -> None:
        self.color = color
        self.size = size
        
class Font:
    def __init__(
        self,
        name:str='Arial',
        size:int=12,
        style:str='normal',
        align:str='center',
    ) -> None:
        self.name = name
        self.size = size
        self.style = style
        self.align = align

class Point:
    '''A point in 2D space.'''
    def __init__(
        self,
        x: float = 0,
        y: float = 0,
        brush: Pen = Pen(),
        pen: Pen = Pen(),
    ) -> None:
        self.x = x
        self.y = y
        self.brush = brush # for drawing the point/line etc.
        self.pen = pen     # for drawing the annotation
        
    def __repr__(self) -> str:
        return f"Point({self.x}, {self.y})"
    
    def move_to(self) -> None:
        '''Move the turtle to the point's location.'''
        t.penup()
        t.goto(self.x, self.y)
        t.pendown()
    
    def offset(self, x: float=0, y: float=0) -> None:
        '''
        Offset the point by the given amounts.
        param x: The amount to offset the x coordinate.
        param y: The amount to offset the y coordinate.
        '''
        self.x += x
        self.y += y
        
    def draw(self) -> None:
        '''Draw the point on the canvas.'''
        self.move_to()
        t.pencolor(self.brush.color)
        t.dot(self.brush.size)
          
class Annotation:
    '''An annotation to be written on the canvas.'''
    def __init__(
        self,
        content:str,
        location:Point = Point(),
        pen: Pen = Pen(),
        font: Font = Font(),
    ) -> None:
        self.content = content
        self.location = location
        self.pen = pen
        self.font = font
        
    def write(self) -> None:
        '''Write the annotation to the canvas.'''
        self.location.move_to()
        t.pencolor(self.pen.color)
        t.write(self.content, align=self.font.align, font=(self.font.name, self.font.size, self.font.style))

class Line:
    '''A line in 2D space.'''
    def __init__(
        self, 
        start: Point, 
        end: Point,
        brush: Pen = Pen(),
        style: str = 'solid',
        ) -> None:
        self.start = start
        self.end = end    
        self.brush = brush
        self.style = style
        
    def draw(self) -> None:
        '''Draw the line on the canvas.'''

        if self.style == 'dotted':
            self._draw_by_dotted()
        elif self.style == 'dashed':
            self._draw_by_dashed()
        elif self.style == 'solid':
            self._draw_by_solid()
        elif self.style == 'double':
            self._draw_by_double()
    
    def offset(self, x: float=0, y: float=0) -> None:
        '''
        Offset the line by the given amounts.
        param x: The amount to offset the x coordinate.
        param y: The amount to offset the y coordinate.
        '''
        self.start.offset(x, y)
        self.end.offset(x, y)
        
    def distance(self) -> float:
        '''Calculate the distance between the start and end points.'''
        return ((self.start.x - self.end.x) ** 2 + (self.start.y - self.end.y) ** 2) ** 0.5
    
    def center_point(self) -> Point:
        '''Calculate the center point of the line.'''
        return Point((self.start.x + self.end.x) / 2, (self.start.y + self.end.y) / 2)

    def angle(self) -> float:
        '''Calculate the angle of the line.'''
        return math.degrees(math.atan2(self.end.y - self.start.y, self.end.x - self.start.x))
    
    def calculate_second_point(
        self,
        first_point: Point,
        distance: float,
        angle: float,
        ) -> Point:
        """
        Calculate the second point of a line segment by specifying the first point and distance, angle.
        """
        # Convert angle to radians
        angle_rad = math.radians(angle)
        
        # Calculate endpoint coordinates
        second_x = first_point.x + distance * math.cos(angle_rad)
        second_y = first_point.y + distance * math.sin(angle_rad)
        
        # Return endpoint as Point
        return Point(second_x, second_y, brush=self.brush)
    
    def _draw_by_dotted(
        self,
        gap: float = 10,
        ) -> None:
        '''Draw the line by dotted style.'''
        
        # Calculate the cordinates of all dots
        points = []
        start = Point(self.start.x, self.start.y, brush=self.brush)
        end = Point(self.end.x, self.end.y, brush=self.brush)
        points.append(start)
        for i in range(1, int(self.distance() / gap)):
            # Calculate the coordinates of the current dot
            point = self.calculate_second_point(start, gap * i, self.angle())
            points.append(point)    
        points.append(end)
               
        # Draw dots
        for point in points:            
            point.draw()
    
    def _draw_by_solid(self) -> None:
        '''Draw the line by solid style.'''
        self.start.move_to()
        t.pencolor(self.brush.color)
        t.pensize(self.brush.size)
        t.goto(self.end.x, self.end.y)
    
    def _draw_by_dashed(self) -> None:
        '''Draw the line by dashed style.'''
        print('drawing dashed line')
    
    def _draw_by_double(self) -> None:
        '''Draw the line by double style.'''
        print('drawing double line')
                       
def main():
    start = Point(-100, -100)
    end = Point(100, 100)
    line = Line(start, end, style='dotted', brush=Pen(color='red', size=5))
    line.draw()
    

    line.offset(50, 0)
    line.style = 'solid'
    line.brush = Pen(color='blue', size=10)
    line.draw()

    t.mainloop()
    t.done()
    
if __name__ == '__main__':
    main()     
        
        

        